﻿using System;
using System.Linq;
using System.Reflection;
using System.Windows;
using System.Windows.Data;
using System.Windows.Interactivity;
using EventTrigger = System.Windows.Interactivity.EventTrigger;

namespace MugenMvvmToolkit.Behaviors
{
    /// <summary>
    ///     Represents the TriggerAction that updates the source of a binding using property name.
    /// </summary>
    public class UpdateBindingOnPropertyChanged : TriggerAction<FrameworkElement>
    {
        #region Fields

		private DependencyProperty _currentDependencyProperty;
        private FieldInfo _currentFieldInfo;

        #endregion

        #region Properties

        /// <summary>
        ///     Gets the dependency PropertyNameProperty property.
        /// </summary>
        public static readonly DependencyProperty PropertyNameProperty =
            DependencyProperty.Register("PropertyName", typeof (string), typeof (UpdateBindingOnPropertyChanged),
                new PropertyMetadata(default(string)));

        /// <summary>
        ///     Gets or sets the property name that you want to update.
        /// </summary>
        public string PropertyName
        {
            get { return (string) GetValue(PropertyNameProperty); }
            set { SetValue(PropertyNameProperty, value); }
        }

        #endregion
        
        #region Overrides of TriggerAction

        /// <summary>
        ///     Invokes the action.
        /// </summary>
        /// <param name="parameter">
        ///     The parameter to the action. If the action does not require a parameter, the parameter may be
        ///     set to a null reference.
        /// </param>
        protected override void Invoke(object parameter)
        {
            BindingExpression bindingExpression = AssociatedObject.GetBindingExpression(GetProperty());
            if (bindingExpression == null)
                return;
            bindingExpression.UpdateSource();
        }

        #endregion

        #region Methods

        private DependencyProperty GetProperty()
        {
            string propertyName = PropertyName + "Property";
            if (_currentFieldInfo != null && _currentFieldInfo.Name == propertyName)
                return _currentDependencyProperty;
            if (AssociatedObject == null)
                throw new NullReferenceException("AssociatedObject is null.");
            if (string.IsNullOrEmpty(propertyName))
                throw new NullReferenceException("SourceProperty is null.");
            _currentFieldInfo = AssociatedObject
                .GetType()
                .GetField(propertyName, BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy);
            if (_currentFieldInfo == null)
                throw new NullReferenceException("Property not found in associated object, property name: " +
                                                 PropertyName);
            _currentDependencyProperty = (DependencyProperty)_currentFieldInfo.GetValue(null);
            return _currentDependencyProperty;
        }

        #endregion
    }

    /// <summary>
    ///     Represents the custom EventTrigger that updates the source of a binding on a text box as the text changes.
    /// </summary>
    public class UpdateTextBindingOnPropertyChanged : EventTrigger
    {
        #region Attached properties

        /// <summary>
        /// If true updates the source of a binding on a text box as the text changes otherwise false.
        /// </summary>
        public static readonly DependencyProperty IsEnabledProperty =
            DependencyProperty.RegisterAttached("IsEnabled", typeof (bool), typeof (UpdateTextBindingOnPropertyChanged), new PropertyMetadata(OnIsEnabledChanged));

        /// <summary>
        /// Sets the IsEnabled property.
        /// </summary>
        public static void SetIsEnabled(UIElement element, bool value)
        {
            element.SetValue(IsEnabledProperty, value);
        }

        /// <summary>
        /// Gets the IsEnabled property.
        /// </summary>
        public static bool GetIsEnabled(UIElement element)
        {
            return (bool) element.GetValue(IsEnabledProperty);
        }

        private static void OnIsEnabledChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args)
        {
            var triggerCollection = Interaction.GetTriggers(dependencyObject);
            var newValue = (bool) args.NewValue;
            UpdateTextBindingOnPropertyChanged trigger =
                triggerCollection.OfType<UpdateTextBindingOnPropertyChanged>().SingleOrDefault();
            if (newValue)
            {
                if (trigger == null)
                    triggerCollection.Add(new UpdateTextBindingOnPropertyChanged());
            }
            else if (trigger != null)
                triggerCollection.Remove(trigger);
            dependencyObject.SetValue(Interaction.TriggersProperty, triggerCollection);
        }

        #endregion

        #region Constructors

        /// <summary>
        ///     Initializes a new instance of the <see cref="UpdateTextBindingOnPropertyChanged" /> class.
        /// </summary>
        public UpdateTextBindingOnPropertyChanged()
        {
            Actions.Add(new UpdateBindingOnPropertyChanged { PropertyName = "Text" });
        }

        #endregion

        #region Overrides of EventTrigger

        /// <summary>
        ///     Specifies the name of the Event this EventTriggerBase is listening for.
        /// </summary>
        /// <returns />
        protected override string GetEventName()
        {
            return "TextChanged";
        }

        #endregion
    }
}